package com.bvgol.examples.springboot.redis.logic;/**
 * @Classname LogicServiceImpl
 * @Description TODO
 * @Date 2021/1/4 14:05
 * @Created by GUOCHEN
 */

import com.alibaba.fastjson.JSON;
import com.bvgol.examples.springboot.redis.entity.Book;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @program: my-spring-completely
 * @description:
 * @author: GUOCHEN
 * @create: 2021/01/04 14:05
 */
@Service
@AllArgsConstructor
public class LogicServiceImpl implements LogicService {
    private RedisTemplate redisTemplate;

    private static final String TOKEN_PREFIX = "order:token:";

    @Override
    public Book logic(Integer token) throws Exception {
        /**
         * 此种实现完整描述(交互式)
         * 在生成这个数据对象的时候
         *      生成一个唯一标志，防止重复提交（响应到页面有一份，有一份保存到redis中）
         * 使用(提交/更新)时
         *      查询redis中有没有token信息, 有，则是第一次提交，放行并删除redis中的orderToken
         *      没有,不合规的数据`返回
         *★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
         *★★★★特别注意`这种方式 实现简单`但是需要结合业务上下文★★★★
         *★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
         * @date: 2021/1/4 15:09
         */
        // 生成一个唯一标志，防止重复提交（响应到页面有一份，有一份保存到redis中）
        //String orderToken = IdWorker.getIdStr();
        // 获取orderToken
//        String orderToken = submitVO.getOrderToken();
        String orderToken = token.toString();
        // 1. 防重复提交，查询redis中有没有orderToken信息，
        //      有，则是第一次提交，放行并删除redis中的orderToken
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long flag = (Long) this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(TOKEN_PREFIX + orderToken), orderToken);
        if (flag == 0) {
            throw new Exception("订单不可重复提交！");
        }
        return null;
    }

    /**
     * 功能描述: 同步接口拆异步
     *
     * @param uuid
     * @param logicArgs
     * @return: java.lang.String
     * @author: 郭辰
     * @date: 2021/3/5 15:38
     */
    @Override
    public String synchronizeToAsync(String uuid, String logicArgs) {
        Instant start = Instant.now();
        List<String> strings = Arrays.asList("16", "18", "19");
        if (strings.contains(logicArgs)) {
            uuid = String.join("::", Arrays.asList("ocr", Instant.now().toString(), uuid));
            String finalUuid = uuid;
            //Thread thread 新写法  java8 thread写法
            new Thread(() -> mockSynchronizeToAsyncResult2(finalUuid, logicArgs)).start();

            long millis = Duration.between(start, Instant.now()).toMillis();
            return "参数无误,正在烧苗!" + millis + "---/t" + finalUuid;
        } else {
            return "参数异常";
        }

    }

    /**
     * 功能描述: 同步接口拆异步 接口返回
     *
     * @param uuid@return: java.lang.String
     * @author: 郭辰
     * @date: 2021/3/5 15:39
     */
    @Override
    public String synchronizeToAsyncResult(String uuid) {
        System.out.println(uuid);
        Object o = this.redisTemplate.opsForValue().get(uuid);
        if (o == null) {
            return "还没有扫描完毕,请等待";
        } else {
            return o.toString();
        }
    }

    /**
     * 功能描述: 模拟异步方法执行
     *
     * @param uuid
     * @param logicArgs
     * @return: void
     * @author: 郭辰
     * @date: 2021/3/5 15:40
     */
    @SneakyThrows
//    @Override
//    @Async
    public void mockSynchronizeToAsyncResult2(String uuid, String logicArgs) {

        System.out.println("收到~收到~");
        for (int i = 0; i < 60; i++) {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("老子在干活唠`莫得催~~莫得催~~~");
        }
        System.out.println("催个鸡儿~!这不是干完了?!!!");

        redisTemplate.opsForValue().set(uuid, "哈哈哈,老子就是不干活~!", 60, TimeUnit.SECONDS);
    }

    @Override
    public void mockSynchronizeToAsyncResult(String uuid, String logicArgs) {

    }

    @Override
    public String md5(Book book) {

        //只要数据内容没变,就不会执行
        String args_key = stringToMD5(JSON.toJSONString(book));

        Object o = redisTemplate.opsForValue().get(args_key);

        if (o != null) {
            return "订单不可重复提交！";
        } else {
            //此方式防止的是短时间内点击,所以30分钟够用
            redisTemplate.opsForValue().set(args_key, args_key, 30, TimeUnit.SECONDS);
            return "非重复操作可以执行";
        }
    }

    public static String stringToMD5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("md5算法未匹配");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }

    /**
     * 当前方法的结果需要缓存,如果缓存中有,方法不调用.如果缓存中没有,会调用方法
     * category1名字是因为gulimall中有用用到category
     * @return
     */
    @Override
    @Cacheable(value = {"category1"},key = "'categorykey'")
    public String redisCacheAble(){
        return "这是CacheAble来的";
    }
    @Override
    @Cacheable(value = {"category2"},key = "#root.methodName")
    public String redisCacheAble2(){
        return "redisCacheAble2";
    }

    @Override
    @CacheEvict(value = {"category1"},key = "'categorykey'")
    public String redisCacheEvict(){
        return "这是CacheAble来的2";
    }
    @Override
    @CacheEvict(value = {"category2"},allEntries = true)
    public String redisCacheEvict2(){
        return "这是CacheAble来的2";
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = {"category1"},key = "'categorykey'"),
            @CacheEvict(value = {"category2"},key = "#root.methodName")
    })
    public String redisCaching(){
        return "这是CacheAble来的2";
    }

}
